package com.itcam;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * @author : Cammy.Wu
 * Description : 
 */

public class LazyMan {

    // todo 4
    private static boolean cammy = false;

    // 构造器私有
    private LazyMan() {
        if (cammy == false) {
            cammy = true;
        } else {
            throw new RuntimeException("单例模式不允许通过反射创建实例");
        }
        // todo 2
        // 防止反射破解单例
//        if (lazyMan != null) { // 升级成三层检测锁  第三层检测：防止单例被破坏
//            throw new RuntimeException("单例模式不允许通过反射创建实例");
//        }
        System.out.println(Thread.currentThread().getName() + "success");
    }

    // 双重检测锁加原子性操作
    private volatile static LazyMan lazyMan;

    // 双重检测锁模式的懒汉式单例 —— DCL懒汉式
    public static LazyMan getInstance() {
        if (lazyMan == null) {  // 第一次检测：为了减少线程对同步锁的竞争
            synchronized (LazyMan.class) { // 一个锁
                if (lazyMan == null) { // 第二次检测：保证单例
                    lazyMan = new LazyMan(); // 不是原子性操作
                    /*
                        不是原子性操作会经过的步骤
                            1.分配内存空间
                            2.执行构造方法，初始化对象
                            3，把对象指向这个空间
                         注意：可能会出现指令重排的现象
                                volatile只保证可见性，不保证原子性，但是可以防止指令重排
                     */
                }

            }
        }
        return lazyMan;
    }

    // 单线程的时候时可以的
    // 多线程则不行
//    public static void main(String[] args) {
//        for (int i = 0; i < 10; i++) {
//            new Thread(() -> {
//                LazyMan.getInstance();
//            }).start();
//        }
//    }


    // 反射
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//        LazyMan instance1 = LazyMan.getInstance();
        // todo 1
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        LazyMan instance2 = declaredConstructor.newInstance();

        // todo 5
        Field cammy = LazyMan.class.getDeclaredField("cammy");
        cammy.setAccessible(true);
        cammy.set(instance2, false);

        // todo 3
        LazyMan instance3 = declaredConstructor.newInstance();

        // 按理来说，单例的对象只能有一个，但是通过反射破解了
//        System.out.println("instance1 = " + instance1);
        System.out.println("instance2 = " + instance2);
        System.out.println("instance2 = " + instance3);


    }
}
